/**
  ******************************************************************************
  * @file    btnlib.c
  * @author  siyun.chen
  * @version v1.0.0
  * @date    
  * @brief   嵌入式按钮处理库实现文件
  ******************************************************************************
  * Change Logs:
  * 2025-10-27      siyun.chen      first version
  ******************************************************************************
  *
  * MIT License
  *
  * Copyright (c) 2025 siyun.chen
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  ******************************************************************************
  */

#include "btnlib.h"

static btn_t *btnList = NULL;
static void btn_handler(btn_t *obj);

/**
 * @brief 按钮注册
 * @param obj 按钮对象
 * @param read_io_pressed 读取IO状态函数
 * @param context 读取函数的上下文参数
 */
void btn_register(btn_t *obj, bool (*read_io_pressed)(void *context), void *context)
{
    if (obj == NULL || read_io_pressed == NULL)
        return;

    memset(obj, 0, sizeof(btn_t));
    obj->read_io_pressed = read_io_pressed;
    obj->context = context;

    // 链表头插入
    if (btnList == NULL)
    {
        btnList = obj;
        return;
    }

    // 链表尾插，若已存在则返回
    btn_t *iterating = btnList;
    while (iterating)
    {
        if (iterating == obj)
            return;

        if (iterating->next == NULL)
        {
            iterating->next = obj;
            return;
        }

        iterating = iterating->next;
    }
    return;
}

/**
 * @brief 按钮注销
 * @param obj 按钮对象
 */
void btn_unregister(btn_t *obj)
{
    if (obj == NULL || btnList == NULL)
        return;

    // 如果要删除的是第一个节点
    if (btnList == obj)
    {
        btnList = obj->next;
        return;
    }

    // 遍历链表查找要删除的节点
    btn_t *iterating = btnList;
    while (iterating->next)
    {
        if (iterating->next == obj)
        {
            iterating->next = obj->next;
            return;
        }
        iterating = iterating->next;
    }
}

/**
 * @brief 按钮事件回调添加
 * @param obj 按钮对象
 * @param event 按钮事件
 * @param cb 回调函数
 */
void btn_add_callback(btn_t *obj, btn_event_t event, btn_callback_func cb)
{
    if (obj == NULL || cb == NULL || event >= BTN_EVENT_TOTAL)
        return;
    obj->cb[event] = cb;
}

/**
 * @brief 按钮事件处理
 * @note 请确保在主循环中以固定周期BTN_PROCESS_PERIOD调用本函数，以实现可靠的按钮事件检测与处理
 */
void btn_process(void)
{
    if (btnList == NULL)
        return;

    for (btn_t *iterating = btnList; iterating; iterating = iterating->next)
        btn_handler(iterating);
}

enum
{
    BTN_STATE_IDLE,
    BTN_STATE_PRESSED,
    BTN_STATE_SINGLE,
    BTN_STATE_DOUBLE,
    BTN_STATE_LONG_PRESS,
    BTN_STATE_LONG_PRESS_HOLD,
};

#define _btn_event_callback(obj, event) \
    do                                  \
    {                                   \
        if (obj->cb[event])             \
            obj->cb[event](obj, event); \
    } while (0)

static void btn_handler(btn_t *obj)
{
    if (obj->current_pressed != obj->read_io_pressed(obj->context))
    {
        obj->debounce_cnt++;
        if (obj->debounce_cnt >= BTN_DEBOUNCE_NUM)
        {
            obj->current_pressed = !obj->current_pressed;
            obj->debounce_cnt = 0;
        }
    }
    else
    {
        obj->debounce_cnt = 0;
    }

    if (obj->state != BTN_STATE_IDLE && obj->scan_cnt < UINT16_MAX)
        obj->scan_cnt++;

    // 状态机超时复位机制，防止状态机卡死
    if (obj->scan_cnt > BTN_STATE_TIMEOUT_THRESHOLD)
    {
        obj->state = BTN_STATE_IDLE;
        obj->scan_cnt = 0;
        obj->debounce_cnt = 0;
    }

    switch (obj->state)
    {
    case BTN_STATE_IDLE:
        if (obj->current_pressed)
        {   // 空闲状态：发生第一次PRESSED事件
            _btn_event_callback(obj, BTN_EVENT_PRESSED);
            obj->scan_cnt = 0;
            obj->state = BTN_STATE_PRESSED;
        }
        break;

    case BTN_STATE_PRESSED:
        if (obj->scan_cnt > BTN_LONG_PRESS_THRESHOLD)
        {   // 第一次PRESSED事件：按压时间超过长按阈值，仍然没有释放
            _btn_event_callback(obj, BTN_EVENT_LONG_PRESS_START);
            obj->scan_cnt = 0; // 重置计数器用于计算长按保持事件间隔
            obj->state = BTN_STATE_LONG_PRESS;
        }
        else if (!obj->current_pressed)
        {   // 第一次PRESSED事件：按压时间没有超过长按阈值
            _btn_event_callback(obj, BTN_EVENT_RELEASED);
            obj->scan_cnt = 0;
            obj->state = BTN_STATE_SINGLE;
        }
        break;

    case BTN_STATE_SINGLE:
        if ((!obj->cb[BTN_EVENT_DOUBLE]) || (obj->scan_cnt > BTN_DOUBLE_HIT_THRESHOLD))
        {   // 不需要识别DOUBLE事件或者双击阈值内没有再次按下，则直接触发单击事件, 并返回初始状态
            _btn_event_callback(obj, BTN_EVENT_SINGLE);
            obj->state = BTN_STATE_IDLE;
            break;
        }
        else if (obj->current_pressed)
        {   // 需要识别DOUBLE事件且双击阈值内再次按下，则触发PRESSED事件，但不立即触发双击事件
            _btn_event_callback(obj, BTN_EVENT_PRESSED);
            obj->scan_cnt = 0;
            obj->state = BTN_STATE_DOUBLE;
        }
        break;

    case BTN_STATE_DOUBLE:
        if (!obj->current_pressed)
        {   // 等待释放按键，此时触发双击事件
            _btn_event_callback(obj, BTN_EVENT_RELEASED);
            _btn_event_callback(obj, BTN_EVENT_DOUBLE);
            obj->state = BTN_STATE_IDLE;
        }
        else if (obj->scan_cnt > BTN_LONG_PRESS_THRESHOLD)
        {   // 如果在双击的第二次按下后长按，则转为长按状态
            _btn_event_callback(obj, BTN_EVENT_LONG_PRESS_START);
            obj->scan_cnt = 0; // 重置计数器用于计算长按保持事件间隔
            obj->state = BTN_STATE_LONG_PRESS;
        }
        break;

    case BTN_STATE_LONG_PRESS: // 进入长按事件
        if (obj->current_pressed)
        {   // 如果按键未松开，每隔一定时间触发一次HOLD事件
            if (obj->scan_cnt >= BTN_LONG_PRESS_HOLD_PERIOD)
            {
                _btn_event_callback(obj, BTN_EVENT_LONG_PRESS_HOLD);
                obj->scan_cnt = 0; // 重置计数器重新计时
            }
        }
        else
        {   // 按键已松开，触发UP事件
            _btn_event_callback(obj, BTN_EVENT_RELEASED);
            _btn_event_callback(obj, BTN_EVENT_LONG_PRESS_STOP);
            obj->state = BTN_STATE_IDLE;
        }
        break;

    default:
        obj->state = BTN_STATE_IDLE;
        break;
    }
}
#undef _btn_event_callback
